{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# A Transfer Learning and Optimized CNN Based Intrusion Detection System for Internet of Vehicles \n",
    "This is the code for the paper entitled \"**A Transfer Learning and Optimized CNN Based Intrusion Detection System for Internet of Vehicles**\" published in **IEEE International Conference on Communications (IEEE ICC)**, doi=[10.1109/ICC45855.2022.9838780](https://ieeexplore.ieee.org/document/9838780).    \n",
    "Authors: Li Yang (lyang339@uwo.ca) and Abdallah Shami (Abdallah.Shami@uwo.ca)  \n",
    "Organization: The Optimized Computing and Communications (OC2) Lab, ECE Department, Western University\n",
    "\n",
    "**Notebook 3: Ensemble Models**  \n",
    "Aims:  construct three ensemble techniques: Bagging, Probability averaging, and Concatenation, to further improve prediction accuracy  \n",
    "* Bagging: use majority voting of top single models  \n",
    "* Probability averaging: calculate the average probability of the single model prediction results (the last layer of CNN models), and select the largest probability class to be the final class  \n",
    "* Concatenation: extract the features in the last several layers of single models, and concatenate together to generate the new layers, and add a dense layer to do prediction"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Import libraries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using TensorFlow backend.\n"
     ]
    }
   ],
   "source": [
    "import warnings\n",
    "warnings.filterwarnings(\"ignore\")\n",
    "import keras\n",
    "from keras.models import Model,load_model\n",
    "from keras import Input\n",
    "from keras.layers import concatenate,Dense,Flatten,Dropout\n",
    "from keras.preprocessing.image import  ImageDataGenerator\n",
    "import keras.callbacks as kcallbacks\n",
    "import os\n",
    "import math\n",
    "from keras.utils import plot_model\n",
    "from keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint, LearningRateScheduler\n",
    "from keras.optimizers import SGD\n",
    "import operator\n",
    "import numpy as np\n",
    "from PIL import Image\n",
    "from collections import defaultdict"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Read the test set"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Found 5845 images belonging to 5 classes.\n"
     ]
    }
   ],
   "source": [
    "#generate images from train set and validation set\n",
    "TARGET_SIZE=(224,224)\n",
    "INPUT_SIZE=(224,224,3)\n",
    "BATCHSIZE=128\n",
    "\n",
    "test_datagen = ImageDataGenerator(rescale=1./255)\n",
    "\n",
    "\n",
    "validation_generator = test_datagen.flow_from_directory(\n",
    "        './test_224/',\n",
    "        target_size=TARGET_SIZE,\n",
    "        batch_size=BATCHSIZE,\n",
    "        class_mode='categorical')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "#generate labels indicating disease (1) or normal (0)\n",
    "label=validation_generator.class_indices\n",
    "label={v: k for k, v in label.items()}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0: '0', 1: '1', 2: '2', 3: '3', 4: '4'}\n"
     ]
    }
   ],
   "source": [
    "print(label)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 ./test_224/0\\100015.png\n"
     ]
    }
   ],
   "source": [
    "#read images from validation folder\n",
    "rootdir = './test_224/'\n",
    "test_laels = []\n",
    "test_images=[]\n",
    "for subdir, dirs, files in os.walk(rootdir):\n",
    "    for file in files:\n",
    "        if not (file.endswith(\".jpeg\"))|(file.endswith(\".jpg\"))|(file.endswith(\".png\")):\n",
    "            continue\n",
    "        test_laels.append(subdir.split('/')[-1])\n",
    "        test_images.append(os.path.join(subdir, file))\n",
    "        \n",
    "print(test_laels[0],test_images[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load 5 trained CNN models"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    " #load model 1: xception\n",
    "xception_model=load_model('./xception.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    " #load model 2: VGG16\n",
    "vgg_model=load_model('./VGG16.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    " #load model 3: VGG19\n",
    "vgg19_model=load_model('./VGG19.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    " #load model 4: inception\n",
    "incep_model=load_model('./inception.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    " #load model 5: inceptionresnet\n",
    "inres_model=load_model('./inceptionresnet.h5')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Use the original CNN base models to make predictions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Xception"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Predicted result for the first image: 0\n",
      "Confidence level: 1.0\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x2167fec8438>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "#Single image prediction\n",
    "import cv2\n",
    "import matplotlib.pyplot as plt\n",
    "test=cv2.imread(test_images[0])\n",
    "\n",
    "img_show=test[:,:,[2,1,0]]\n",
    "test=test/255.\n",
    "test_shape=(1,)+test.shape\n",
    "test=test.reshape(test_shape)\n",
    "\n",
    "res=xception_model.predict(test)\n",
    "\n",
    "prob=res[0,np.argmax(res,axis=1)[0]]\n",
    "res=label[np.argmax(res,axis=1)[0]]\n",
    "print('Predicted result for the first image: %s'%res)\n",
    "print('Confidence level: %s'%prob)\n",
    "plt.imshow(img_show)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Wall time: 1min 26s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "import time\n",
    "predict=[]\n",
    "length=len(test_images)\n",
    "t1 = time.time()\n",
    "for i in range(length):\n",
    "    inputimg=test_images[i]\n",
    "    test_batch=[]\n",
    "    thisimg=np.array(Image.open(inputimg))/255 #read all the images in validation set\n",
    "    #print(thisimg)\n",
    "    test_shape=(1,)+thisimg.shape\n",
    "    thisimg=thisimg.reshape(test_shape)\n",
    "    xception_model_batch=xception_model.predict(thisimg) #use master model to process the input image\n",
    "    #generate result by model 1\n",
    "    prob=xception_model_batch[0,np.argmax(xception_model_batch,axis=1)[0]]\n",
    "    res=label[np.argmax(xception_model_batch,axis=1)[0]]\n",
    "    predict.append(res)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Xception accuracy: 1.0\n",
      "precision: 1.0\n",
      "recall: 1.0\n",
      "f1: 1.0\n",
      "[[5052    0    0    0    0]\n",
      " [   0  225    0    0    0]\n",
      " [   0    0  200    0    0]\n",
      " [   0    0    0  197    0]\n",
      " [   0    0    0    0  171]]\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       1.00      1.00      1.00      5052\n",
      "           1       1.00      1.00      1.00       225\n",
      "           2       1.00      1.00      1.00       200\n",
      "           3       1.00      1.00      1.00       197\n",
      "           4       1.00      1.00      1.00       171\n",
      "\n",
      "    accuracy                           1.00      5845\n",
      "   macro avg       1.00      1.00      1.00      5845\n",
      "weighted avg       1.00      1.00      1.00      5845\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score\n",
    "acc=accuracy_score(test_laels,predict)\n",
    "pre=precision_score(test_laels,predict,average='weighted')\n",
    "re=recall_score(test_laels,predict,average='weighted')\n",
    "f1=f1_score(test_laels,predict,average='weighted')\n",
    "print('Xception accuracy: %s'%acc)\n",
    "print('precision: %s'%pre)\n",
    "print('recall: %s'%re)\n",
    "print('f1: %s'%f1)\n",
    "from sklearn.metrics import classification_report, confusion_matrix\n",
    "print(confusion_matrix(test_laels, predict))\n",
    "target_names = ['0', '1','2','3','4']\n",
    "print(classification_report(test_laels, predict, target_names=target_names))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. VGG16"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Wall time: 50 s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "predict=[]\n",
    "length=len(test_images)\n",
    "t1 = time.time()\n",
    "for i in range(length):\n",
    "    inputimg=test_images[i]\n",
    "    test_batch=[]\n",
    "    thisimg=np.array(Image.open(inputimg))/255 #read all the images in validation set\n",
    "    #print(thisimg)\n",
    "    test_shape=(1,)+thisimg.shape\n",
    "    thisimg=thisimg.reshape(test_shape)\n",
    "    vgg_model_batch=vgg_model.predict(thisimg) #use master model to process the input image\n",
    "    #generate result by model 1\n",
    "    prob=vgg_model_batch[0,np.argmax(vgg_model_batch,axis=1)[0]]\n",
    "    res=label[np.argmax(vgg_model_batch,axis=1)[0]]\n",
    "    predict.append(res)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "VGG16 accuracy: 1.0\n",
      "precision: 1.0\n",
      "recall: 1.0\n",
      "f1: 1.0\n",
      "[[5052    0    0    0    0]\n",
      " [   0  225    0    0    0]\n",
      " [   0    0  200    0    0]\n",
      " [   0    0    0  197    0]\n",
      " [   0    0    0    0  171]]\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       1.00      1.00      1.00      5052\n",
      "           1       1.00      1.00      1.00       225\n",
      "           2       1.00      1.00      1.00       200\n",
      "           3       1.00      1.00      1.00       197\n",
      "           4       1.00      1.00      1.00       171\n",
      "\n",
      "    accuracy                           1.00      5845\n",
      "   macro avg       1.00      1.00      1.00      5845\n",
      "weighted avg       1.00      1.00      1.00      5845\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score\n",
    "acc=accuracy_score(test_laels,predict)\n",
    "pre=precision_score(test_laels,predict,average='weighted')\n",
    "re=recall_score(test_laels,predict,average='weighted')\n",
    "f1=f1_score(test_laels,predict,average='weighted')\n",
    "print('VGG16 accuracy: %s'%acc)\n",
    "print('precision: %s'%pre)\n",
    "print('recall: %s'%re)\n",
    "print('f1: %s'%f1)\n",
    "from sklearn.metrics import classification_report, confusion_matrix\n",
    "print(confusion_matrix(test_laels, predict))\n",
    "target_names = ['0', '1','2','3','4']\n",
    "print(classification_report(test_laels, predict, target_names=target_names))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. VGG19"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Wall time: 56.6 s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "predict=[]\n",
    "length=len(test_images)\n",
    "t1 = time.time()\n",
    "for i in range(length):\n",
    "    inputimg=test_images[i]\n",
    "    test_batch=[]\n",
    "    thisimg=np.array(Image.open(inputimg))/255 #read all the images in validation set\n",
    "    #print(thisimg)\n",
    "    test_shape=(1,)+thisimg.shape\n",
    "    thisimg=thisimg.reshape(test_shape)\n",
    "    vgg19_model_batch=vgg19_model.predict(thisimg) #use master model to process the input image\n",
    "    #generate result by model 1\n",
    "    prob=vgg19_model_batch[0,np.argmax(vgg19_model_batch,axis=1)[0]]\n",
    "    res=label[np.argmax(vgg19_model_batch,axis=1)[0]]\n",
    "    predict.append(res)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "VGG19 accuracy: 1.0\n",
      "precision: 1.0\n",
      "recall: 1.0\n",
      "f1: 1.0\n",
      "[[5052    0    0    0    0]\n",
      " [   0  225    0    0    0]\n",
      " [   0    0  200    0    0]\n",
      " [   0    0    0  197    0]\n",
      " [   0    0    0    0  171]]\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       1.00      1.00      1.00      5052\n",
      "           1       1.00      1.00      1.00       225\n",
      "           2       1.00      1.00      1.00       200\n",
      "           3       1.00      1.00      1.00       197\n",
      "           4       1.00      1.00      1.00       171\n",
      "\n",
      "    accuracy                           1.00      5845\n",
      "   macro avg       1.00      1.00      1.00      5845\n",
      "weighted avg       1.00      1.00      1.00      5845\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score\n",
    "acc=accuracy_score(test_laels,predict)\n",
    "pre=precision_score(test_laels,predict,average='weighted')\n",
    "re=recall_score(test_laels,predict,average='weighted')\n",
    "f1=f1_score(test_laels,predict,average='weighted')\n",
    "print('VGG19 accuracy: %s'%acc)\n",
    "print('precision: %s'%pre)\n",
    "print('recall: %s'%re)\n",
    "print('f1: %s'%f1)\n",
    "from sklearn.metrics import classification_report, confusion_matrix\n",
    "print(confusion_matrix(test_laels, predict))\n",
    "target_names = ['0', '1','2','3','4']\n",
    "print(classification_report(test_laels, predict, target_names=target_names))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Inception"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Wall time: 2min 19s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "predict=[]\n",
    "length=len(test_images)\n",
    "t1 = time.time()\n",
    "for i in range(length):\n",
    "    inputimg=test_images[i]\n",
    "    test_batch=[]\n",
    "    thisimg=np.array(Image.open(inputimg))/255 #read all the images in validation set\n",
    "    #print(thisimg)\n",
    "    test_shape=(1,)+thisimg.shape\n",
    "    thisimg=thisimg.reshape(test_shape)\n",
    "    incep_model_batch=incep_model.predict(thisimg) #use master model to process the input image\n",
    "    #generate result by model 1\n",
    "    prob=incep_model_batch[0,np.argmax(incep_model_batch,axis=1)[0]]\n",
    "    res=label[np.argmax(incep_model_batch,axis=1)[0]]\n",
    "    predict.append(res)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "inception accuracy: 1.0\n",
      "precision: 1.0\n",
      "recall: 1.0\n",
      "f1: 1.0\n",
      "[[5052    0    0    0    0]\n",
      " [   0  225    0    0    0]\n",
      " [   0    0  200    0    0]\n",
      " [   0    0    0  197    0]\n",
      " [   0    0    0    0  171]]\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       1.00      1.00      1.00      5052\n",
      "           1       1.00      1.00      1.00       225\n",
      "           2       1.00      1.00      1.00       200\n",
      "           3       1.00      1.00      1.00       197\n",
      "           4       1.00      1.00      1.00       171\n",
      "\n",
      "    accuracy                           1.00      5845\n",
      "   macro avg       1.00      1.00      1.00      5845\n",
      "weighted avg       1.00      1.00      1.00      5845\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score\n",
    "acc=accuracy_score(test_laels,predict)\n",
    "pre=precision_score(test_laels,predict,average='weighted')\n",
    "re=recall_score(test_laels,predict,average='weighted')\n",
    "f1=f1_score(test_laels,predict,average='weighted')\n",
    "print('inception accuracy: %s'%acc)\n",
    "print('precision: %s'%pre)\n",
    "print('recall: %s'%re)\n",
    "print('f1: %s'%f1)\n",
    "from sklearn.metrics import classification_report, confusion_matrix\n",
    "print(confusion_matrix(test_laels, predict))\n",
    "target_names = ['0', '1','2','3','4']\n",
    "print(classification_report(test_laels, predict, target_names=target_names))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. InceptionResnet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Wall time: 4min 25s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "predict=[]\n",
    "length=len(test_images)\n",
    "t1 = time.time()\n",
    "for i in range(length):\n",
    "    inputimg=test_images[i]\n",
    "    test_batch=[]\n",
    "    thisimg=np.array(Image.open(inputimg))/255 #read all the images in validation set\n",
    "    #print(thisimg)\n",
    "    test_shape=(1,)+thisimg.shape\n",
    "    thisimg=thisimg.reshape(test_shape)\n",
    "    inres_model_batch=inres_model.predict(thisimg) #use master model to process the input image\n",
    "    #generate result by model 1\n",
    "    prob=inres_model_batch[0,np.argmax(inres_model_batch,axis=1)[0]]\n",
    "    res=label[np.argmax(inres_model_batch,axis=1)[0]]\n",
    "    predict.append(res)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "inceptionresnet accuracy: 0.9998289136013687\n",
      "precision: 0.999829777674089\n",
      "recall: 0.9998289136013687\n",
      "f1: 0.9998288793066084\n",
      "[[5052    0    0    0    0]\n",
      " [   0  225    0    0    0]\n",
      " [   0    0  200    0    0]\n",
      " [   0    0    0  197    0]\n",
      " [   0    0    0    1  170]]\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       1.00      1.00      1.00      5052\n",
      "           1       1.00      1.00      1.00       225\n",
      "           2       1.00      1.00      1.00       200\n",
      "           3       0.99      1.00      1.00       197\n",
      "           4       1.00      0.99      1.00       171\n",
      "\n",
      "    accuracy                           1.00      5845\n",
      "   macro avg       1.00      1.00      1.00      5845\n",
      "weighted avg       1.00      1.00      1.00      5845\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score\n",
    "acc=accuracy_score(test_laels,predict)\n",
    "pre=precision_score(test_laels,predict,average='weighted')\n",
    "re=recall_score(test_laels,predict,average='weighted')\n",
    "f1=f1_score(test_laels,predict,average='weighted')\n",
    "print('inceptionresnet accuracy: %s'%acc)\n",
    "print('precision: %s'%pre)\n",
    "print('recall: %s'%re)\n",
    "print('f1: %s'%f1)\n",
    "from sklearn.metrics import classification_report, confusion_matrix\n",
    "print(confusion_matrix(test_laels, predict))\n",
    "target_names = ['0', '1','2','3','4']\n",
    "print(classification_report(test_laels, predict, target_names=target_names))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "### 6. Resnet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    " #load model 6: resnet\n",
    "res_model=load_model('./resnet.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Wall time: 1min 38s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "predict=[]\n",
    "length=len(test_images)\n",
    "t1 = time.time()\n",
    "for i in range(length):\n",
    "    inputimg=test_images[i]\n",
    "    test_batch=[]\n",
    "    thisimg=np.array(Image.open(inputimg))/255 #read all the images in validation set\n",
    "    #print(thisimg)\n",
    "    test_shape=(1,)+thisimg.shape\n",
    "    thisimg=thisimg.reshape(test_shape)\n",
    "    res_model_batch=res_model.predict(thisimg) #use master model to process the input image\n",
    "    #generate result by model 1\n",
    "    prob=res_model_batch[0,np.argmax(res_model_batch,axis=1)[0]]\n",
    "    res=label[np.argmax(res_model_batch,axis=1)[0]]\n",
    "    predict.append(res)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "resnet accuracy: 0.9662959794696322\n",
      "precision: 0.9338569031275825\n",
      "recall: 0.9662959794696322\n",
      "f1: 0.9497662530625438\n",
      "[[5052    0    0    0    0]\n",
      " [   0  225    0    0    0]\n",
      " [   0    0  200    0    0]\n",
      " [ 197    0    0    0    0]\n",
      " [   0    0    0    0  171]]\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       0.96      1.00      0.98      5052\n",
      "           1       1.00      1.00      1.00       225\n",
      "           2       1.00      1.00      1.00       200\n",
      "           3       0.00      0.00      0.00       197\n",
      "           4       1.00      1.00      1.00       171\n",
      "\n",
      "    accuracy                           0.97      5845\n",
      "   macro avg       0.79      0.80      0.80      5845\n",
      "weighted avg       0.93      0.97      0.95      5845\n",
      "\n",
      "Wall time: 35.9 ms\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score\n",
    "acc=accuracy_score(test_laels,predict)\n",
    "pre=precision_score(test_laels,predict,average='weighted')\n",
    "re=recall_score(test_laels,predict,average='weighted')\n",
    "f1=f1_score(test_laels,predict,average='weighted')\n",
    "print('resnet accuracy: %s'%acc)\n",
    "print('precision: %s'%pre)\n",
    "print('recall: %s'%re)\n",
    "print('f1: %s'%f1)\n",
    "from sklearn.metrics import classification_report, confusion_matrix\n",
    "print(confusion_matrix(test_laels, predict))\n",
    "target_names = ['0', '1','2','3','4']\n",
    "print(classification_report(test_laels, predict, target_names=target_names))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Best performing single model (vgg):  \n",
    "Accuracy: 99.96"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Bagging ensemble"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The testing time is :99.662228 seconds\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "predict=[]\n",
    "length=len(test_images)\n",
    "t1 = time.time()\n",
    "for i in range((length//127)+1):\n",
    "    inputimg=test_images[127*i:127*(i+1)]\n",
    "    test_batch=[]\n",
    "    for path in inputimg:\n",
    "        thisimg=np.array(Image.open(path))/255\n",
    "        test_batch.append(thisimg)\n",
    "    #generate result by model 1\n",
    "    xception_model_batch=xception_model.predict(np.array(test_batch))\n",
    "    xception_model_batch=list(np.argmax(xception_model_batch,axis=1))\n",
    "    xception_model_batch=[label[con] for con in xception_model_batch]\n",
    "#     print(xception_model_batch)\n",
    "    #generate result by model 2\n",
    "    vgg_model_batch=vgg_model.predict(np.array(test_batch))\n",
    "    vgg_model_batch=list(np.argmax(vgg_model_batch,axis=1))\n",
    "    vgg_model_batch=[label[con] for con in vgg_model_batch]\n",
    "#     print(vgg_model_batch)\n",
    "    #generate result by model 3\n",
    "    vgg19_model_batch=vgg19_model.predict(np.array(test_batch))\n",
    "    vgg19_model_batch=list(np.argmax(vgg19_model_batch,axis=1))\n",
    "    vgg19_model_batch=[label[con] for con in vgg19_model_batch]\n",
    "#     print(vgg19_model_batch)\n",
    "    #generate result by model 4\n",
    "    incep_model_batch=incep_model.predict(np.array(test_batch))\n",
    "    incep_model_batch=list(np.argmax(incep_model_batch,axis=1))\n",
    "    incep_model_batch=[label[con] for con in incep_model_batch]\n",
    "#     print(incep_model_batch)\n",
    "    #generate result by model 5\n",
    "    inres_model_batch=inres_model.predict(np.array(test_batch))\n",
    "    inres_model_batch=list(np.argmax(inres_model_batch,axis=1))\n",
    "    inres_model_batch=[label[con] for con in inres_model_batch]\n",
    "#     print(inres_model_batch)\n",
    "    #bagging the three results generated by 3 singular models\n",
    "    predict_batch=[]\n",
    "    for i,j,k,p,q in zip(xception_model_batch,vgg_model_batch,vgg19_model_batch,incep_model_batch,inres_model_batch):\n",
    "        count=defaultdict(int)\n",
    "        count[i]+=1\n",
    "        count[j]+=1\n",
    "        count[k]+=1\n",
    "        count[p]+=1\n",
    "        count[q]+=1\n",
    "        #rank the predicted results in descending order\n",
    "        predict_one=sorted(count.items(), key=operator.itemgetter(1),reverse=True)[0][0]\n",
    "        predict_batch.append(predict_one)\n",
    "#     print('predict:',predict_batch)\n",
    "    predict.append(predict_batch)\n",
    "t2 = time.time()\n",
    "print('The testing time is :%f seconds' % (t2-t1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "predict=sum(predict,[])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "bagging accuracy:1.0\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score\n",
    "acc=accuracy_score(test_laels,predict)\n",
    "print('bagging accuracy:%s'%acc)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[5052    0    0    0    0]\n",
      " [   0  225    0    0    0]\n",
      " [   0    0  200    0    0]\n",
      " [   0    0    0  197    0]\n",
      " [   0    0    0    0  171]]\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       1.00      1.00      1.00      5052\n",
      "           1       1.00      1.00      1.00       225\n",
      "           2       1.00      1.00      1.00       200\n",
      "           3       1.00      1.00      1.00       197\n",
      "           4       1.00      1.00      1.00       171\n",
      "\n",
      "    accuracy                           1.00      5845\n",
      "   macro avg       1.00      1.00      1.00      5845\n",
      "weighted avg       1.00      1.00      1.00      5845\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import classification_report, confusion_matrix\n",
    "print(confusion_matrix(test_laels, predict))\n",
    "target_names = ['0', '1','2','3','4']\n",
    "print(classification_report(test_laels, predict, target_names=target_names))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "After bagging ensemble, the accuracy improved to 0.990"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# Probability Averaging"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import keras\n",
    "from keras.models import Model,load_model\n",
    "from keras import Input\n",
    "from keras.layers import concatenate,Dense,Flatten,Dropout,Average\n",
    "from keras.preprocessing.image import  ImageDataGenerator\n",
    "import keras.callbacks as kcallbacks\n",
    "import os\n",
    "import math\n",
    "from keras.utils import plot_model\n",
    "from keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint, LearningRateScheduler\n",
    "from keras.optimizers import SGD\n",
    "import operator\n",
    "import numpy as np\n",
    "from PIL import Image\n",
    "from collections import defaultdict\n",
    "import tensorflow as tf\n",
    "tf.logging.set_verbosity(tf.logging.ERROR)\n",
    "import os\n",
    "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"0\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The testing time is :2.661651 seconds\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "t1 = time.time()\n",
    "img=Input(shape=(224,224,3),name='img')\n",
    "feature1=xception_model(img)\n",
    "feature2=vgg_model(img)\n",
    "feature3=incep_model(img)\n",
    "for layer in xception_model.layers:  \n",
    "    layer.trainable = False \n",
    "for layer in vgg_model.layers:  \n",
    "    layer.trainable = False  \n",
    "for layer in incep_model.layers:  \n",
    "    layer.trainable = False  \n",
    "output=Average()([feature1,feature2,feature3]) #add the confidence lists generated by 3 models\n",
    "model=Model(inputs=img,outputs=output)\n",
    "\n",
    "#the optimization function\n",
    "opt = keras.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-08)\n",
    "model.compile(loss='categorical_crossentropy',\n",
    "              optimizer=opt,\n",
    "              metrics=['accuracy'])\n",
    "t2 = time.time()\n",
    "print('The testing time is :%f seconds' % (t2-t1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 ./test_224/0\\100015.png\n"
     ]
    }
   ],
   "source": [
    "#read images from validation folder\n",
    "rootdir = './test_224/'\n",
    "test_laels = []\n",
    "test_images=[]\n",
    "for subdir, dirs, files in os.walk(rootdir):\n",
    "    for file in files:\n",
    "        if not (file.endswith(\".jpeg\"))|(file.endswith(\".jpg\"))|(file.endswith(\".png\")):\n",
    "            continue\n",
    "        test_laels.append(subdir.split('/')[-1])\n",
    "        test_images.append(os.path.join(subdir, file))\n",
    "        \n",
    "print(test_laels[0],test_images[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The testing time is :51.721883 seconds\n"
     ]
    }
   ],
   "source": [
    "#test the averaging model on the validation set\n",
    "import time\n",
    "predict=[]\n",
    "length=len(test_images)\n",
    "t1 = time.time()\n",
    "for i in range((length//127)+1):\n",
    "    inputimg=test_images[127*i:127*(i+1)]\n",
    "    test_batch=[]\n",
    "    for path in inputimg:\n",
    "        thisimg=np.array(Image.open(path))/255\n",
    "        test_batch.append(thisimg)\n",
    "    #print(i, np.array(test_batch).shape)\n",
    "    model_batch=model.predict(np.array(test_batch))\n",
    "    predict_batch=list(np.argmax(model_batch,axis=1))\n",
    "    predict_batch=[label[con] for con in predict_batch]\n",
    "    predict.append(predict_batch)\n",
    "\n",
    "predict=sum(predict,[])\n",
    "\n",
    "t2 = time.time()\n",
    "print('The testing time is :%f seconds' % (t2-t1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Probability Averaging accuracy:1.0\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "acc=accuracy_score(test_laels,predict)\n",
    "print('Probability Averaging accuracy:%s'%acc)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[5052    0    0    0    0]\n",
      " [   0  225    0    0    0]\n",
      " [   0    0  200    0    0]\n",
      " [   0    0    0  197    0]\n",
      " [   0    0    0    0  171]]\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       1.00      1.00      1.00      5052\n",
      "           1       1.00      1.00      1.00       225\n",
      "           2       1.00      1.00      1.00       200\n",
      "           3       1.00      1.00      1.00       197\n",
      "           4       1.00      1.00      1.00       171\n",
      "\n",
      "    accuracy                           1.00      5845\n",
      "   macro avg       1.00      1.00      1.00      5845\n",
      "weighted avg       1.00      1.00      1.00      5845\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import classification_report, confusion_matrix\n",
    "print(confusion_matrix(test_laels, predict))\n",
    "target_names = ['0', '1','2','3','4']\n",
    "print(classification_report(test_laels, predict, target_names=target_names))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Concatenation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import keras\n",
    "from keras.models import Model,load_model\n",
    "from keras import Input\n",
    "from keras.layers import concatenate,Dense,Flatten,Dropout\n",
    "from keras.preprocessing.image import  ImageDataGenerator\n",
    "import keras.callbacks as kcallbacks\n",
    "import os\n",
    "import math\n",
    "from keras.utils import plot_model\n",
    "from keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint, LearningRateScheduler\n",
    "from keras.optimizers import SGD"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 input_1\n",
      "1 block1_conv1\n",
      "2 block1_conv1_bn\n",
      "3 block1_conv1_act\n",
      "4 block1_conv2\n",
      "5 block1_conv2_bn\n",
      "6 block1_conv2_act\n",
      "7 block2_sepconv1\n",
      "8 block2_sepconv1_bn\n",
      "9 block2_sepconv2_act\n",
      "10 block2_sepconv2\n",
      "11 block2_sepconv2_bn\n",
      "12 conv2d_15\n",
      "13 block2_pool\n",
      "14 batch_normalization_1\n",
      "15 add_1\n",
      "16 block3_sepconv1_act\n",
      "17 block3_sepconv1\n",
      "18 block3_sepconv1_bn\n",
      "19 block3_sepconv2_act\n",
      "20 block3_sepconv2\n",
      "21 block3_sepconv2_bn\n",
      "22 conv2d_16\n",
      "23 block3_pool\n",
      "24 batch_normalization_2\n",
      "25 add_2\n",
      "26 block4_sepconv1_act\n",
      "27 block4_sepconv1\n",
      "28 block4_sepconv1_bn\n",
      "29 block4_sepconv2_act\n",
      "30 block4_sepconv2\n",
      "31 block4_sepconv2_bn\n",
      "32 conv2d_17\n",
      "33 block4_pool\n",
      "34 batch_normalization_3\n",
      "35 add_3\n",
      "36 block5_sepconv1_act\n",
      "37 block5_sepconv1\n",
      "38 block5_sepconv1_bn\n",
      "39 block5_sepconv2_act\n",
      "40 block5_sepconv2\n",
      "41 block5_sepconv2_bn\n",
      "42 block5_sepconv3_act\n",
      "43 block5_sepconv3\n",
      "44 block5_sepconv3_bn\n",
      "45 add_4\n",
      "46 block6_sepconv1_act\n",
      "47 block6_sepconv1\n",
      "48 block6_sepconv1_bn\n",
      "49 block6_sepconv2_act\n",
      "50 block6_sepconv2\n",
      "51 block6_sepconv2_bn\n",
      "52 block6_sepconv3_act\n",
      "53 block6_sepconv3\n",
      "54 block6_sepconv3_bn\n",
      "55 add_5\n",
      "56 block7_sepconv1_act\n",
      "57 block7_sepconv1\n",
      "58 block7_sepconv1_bn\n",
      "59 block7_sepconv2_act\n",
      "60 block7_sepconv2\n",
      "61 block7_sepconv2_bn\n",
      "62 block7_sepconv3_act\n",
      "63 block7_sepconv3\n",
      "64 block7_sepconv3_bn\n",
      "65 add_6\n",
      "66 block8_sepconv1_act\n",
      "67 block8_sepconv1\n",
      "68 block8_sepconv1_bn\n",
      "69 block8_sepconv2_act\n",
      "70 block8_sepconv2\n",
      "71 block8_sepconv2_bn\n",
      "72 block8_sepconv3_act\n",
      "73 block8_sepconv3\n",
      "74 block8_sepconv3_bn\n",
      "75 add_7\n",
      "76 block9_sepconv1_act\n",
      "77 block9_sepconv1\n",
      "78 block9_sepconv1_bn\n",
      "79 block9_sepconv2_act\n",
      "80 block9_sepconv2\n",
      "81 block9_sepconv2_bn\n",
      "82 block9_sepconv3_act\n",
      "83 block9_sepconv3\n",
      "84 block9_sepconv3_bn\n",
      "85 add_8\n",
      "86 block10_sepconv1_act\n",
      "87 block10_sepconv1\n",
      "88 block10_sepconv1_bn\n",
      "89 block10_sepconv2_act\n",
      "90 block10_sepconv2\n",
      "91 block10_sepconv2_bn\n",
      "92 block10_sepconv3_act\n",
      "93 block10_sepconv3\n",
      "94 block10_sepconv3_bn\n",
      "95 add_9\n",
      "96 block11_sepconv1_act\n",
      "97 block11_sepconv1\n",
      "98 block11_sepconv1_bn\n",
      "99 block11_sepconv2_act\n",
      "100 block11_sepconv2\n",
      "101 block11_sepconv2_bn\n",
      "102 block11_sepconv3_act\n",
      "103 block11_sepconv3\n",
      "104 block11_sepconv3_bn\n",
      "105 add_10\n",
      "106 block12_sepconv1_act\n",
      "107 block12_sepconv1\n",
      "108 block12_sepconv1_bn\n",
      "109 block12_sepconv2_act\n",
      "110 block12_sepconv2\n",
      "111 block12_sepconv2_bn\n",
      "112 block12_sepconv3_act\n",
      "113 block12_sepconv3\n",
      "114 block12_sepconv3_bn\n",
      "115 add_11\n",
      "116 block13_sepconv1_act\n",
      "117 block13_sepconv1\n",
      "118 block13_sepconv1_bn\n",
      "119 block13_sepconv2_act\n",
      "120 block13_sepconv2\n",
      "121 block13_sepconv2_bn\n",
      "122 conv2d_18\n",
      "123 block13_pool\n",
      "124 batch_normalization_4\n",
      "125 add_12\n",
      "126 block14_sepconv1\n",
      "127 block14_sepconv1_bn\n",
      "128 block14_sepconv1_act\n",
      "129 block14_sepconv2\n",
      "130 block14_sepconv2_bn\n",
      "131 block14_sepconv2_act\n",
      "132 global_average_pooling2d_3\n",
      "133 dense_5\n",
      "134 dropout_3\n",
      "135 dense_6\n"
     ]
    }
   ],
   "source": [
    "for i,layer in enumerate(xception_model.layers):\n",
    "    print(i,layer.name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 input_2\n",
      "1 block1_conv1\n",
      "2 block1_conv2\n",
      "3 block1_pool\n",
      "4 block2_conv1\n",
      "5 block2_conv2\n",
      "6 block2_pool\n",
      "7 block3_conv1\n",
      "8 block3_conv2\n",
      "9 block3_conv3\n",
      "10 block3_pool\n",
      "11 block4_conv1\n",
      "12 block4_conv2\n",
      "13 block4_conv3\n",
      "14 block4_pool\n",
      "15 block5_conv1\n",
      "16 block5_conv2\n",
      "17 block5_conv3\n",
      "18 block5_pool\n",
      "19 global_average_pooling2d_4\n",
      "20 dense_7\n",
      "21 dropout_4\n",
      "22 dense_8\n"
     ]
    }
   ],
   "source": [
    "for i,layer in enumerate(vgg_model.layers):\n",
    "    print(i,layer.name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 input_3\n",
      "1 block1_conv1\n",
      "2 block1_conv2\n",
      "3 block1_pool\n",
      "4 block2_conv1\n",
      "5 block2_conv2\n",
      "6 block2_pool\n",
      "7 block3_conv1\n",
      "8 block3_conv2\n",
      "9 block3_conv3\n",
      "10 block3_conv4\n",
      "11 block3_pool\n",
      "12 block4_conv1\n",
      "13 block4_conv2\n",
      "14 block4_conv3\n",
      "15 block4_conv4\n",
      "16 block4_pool\n",
      "17 block5_conv1\n",
      "18 block5_conv2\n",
      "19 block5_conv3\n",
      "20 block5_conv4\n",
      "21 block5_pool\n",
      "22 global_average_pooling2d_5\n",
      "23 dense_9\n",
      "24 dropout_5\n",
      "25 dense_10\n"
     ]
    }
   ],
   "source": [
    "for i,layer in enumerate(vgg19_model.layers):\n",
    "    print(i,layer.name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 input_5\n",
      "1 conv2d_19\n",
      "2 batch_normalization_5\n",
      "3 activation_50\n",
      "4 conv2d_20\n",
      "5 batch_normalization_6\n",
      "6 activation_51\n",
      "7 conv2d_21\n",
      "8 batch_normalization_7\n",
      "9 activation_52\n",
      "10 max_pooling2d_6\n",
      "11 conv2d_22\n",
      "12 batch_normalization_8\n",
      "13 activation_53\n",
      "14 conv2d_23\n",
      "15 batch_normalization_9\n",
      "16 activation_54\n",
      "17 max_pooling2d_7\n",
      "18 conv2d_27\n",
      "19 batch_normalization_13\n",
      "20 activation_58\n",
      "21 conv2d_25\n",
      "22 conv2d_28\n",
      "23 batch_normalization_11\n",
      "24 batch_normalization_14\n",
      "25 activation_56\n",
      "26 activation_59\n",
      "27 average_pooling2d_1\n",
      "28 conv2d_24\n",
      "29 conv2d_26\n",
      "30 conv2d_29\n",
      "31 conv2d_30\n",
      "32 batch_normalization_10\n",
      "33 batch_normalization_12\n",
      "34 batch_normalization_15\n",
      "35 batch_normalization_16\n",
      "36 activation_55\n",
      "37 activation_57\n",
      "38 activation_60\n",
      "39 activation_61\n",
      "40 mixed0\n",
      "41 conv2d_34\n",
      "42 batch_normalization_20\n",
      "43 activation_65\n",
      "44 conv2d_32\n",
      "45 conv2d_35\n",
      "46 batch_normalization_18\n",
      "47 batch_normalization_21\n",
      "48 activation_63\n",
      "49 activation_66\n",
      "50 average_pooling2d_2\n",
      "51 conv2d_31\n",
      "52 conv2d_33\n",
      "53 conv2d_36\n",
      "54 conv2d_37\n",
      "55 batch_normalization_17\n",
      "56 batch_normalization_19\n",
      "57 batch_normalization_22\n",
      "58 batch_normalization_23\n",
      "59 activation_62\n",
      "60 activation_64\n",
      "61 activation_67\n",
      "62 activation_68\n",
      "63 mixed1\n",
      "64 conv2d_41\n",
      "65 batch_normalization_27\n",
      "66 activation_72\n",
      "67 conv2d_39\n",
      "68 conv2d_42\n",
      "69 batch_normalization_25\n",
      "70 batch_normalization_28\n",
      "71 activation_70\n",
      "72 activation_73\n",
      "73 average_pooling2d_3\n",
      "74 conv2d_38\n",
      "75 conv2d_40\n",
      "76 conv2d_43\n",
      "77 conv2d_44\n",
      "78 batch_normalization_24\n",
      "79 batch_normalization_26\n",
      "80 batch_normalization_29\n",
      "81 batch_normalization_30\n",
      "82 activation_69\n",
      "83 activation_71\n",
      "84 activation_74\n",
      "85 activation_75\n",
      "86 mixed2\n",
      "87 conv2d_46\n",
      "88 batch_normalization_32\n",
      "89 activation_77\n",
      "90 conv2d_47\n",
      "91 batch_normalization_33\n",
      "92 activation_78\n",
      "93 conv2d_45\n",
      "94 conv2d_48\n",
      "95 batch_normalization_31\n",
      "96 batch_normalization_34\n",
      "97 activation_76\n",
      "98 activation_79\n",
      "99 max_pooling2d_8\n",
      "100 mixed3\n",
      "101 conv2d_53\n",
      "102 batch_normalization_39\n",
      "103 activation_84\n",
      "104 conv2d_54\n",
      "105 batch_normalization_40\n",
      "106 activation_85\n",
      "107 conv2d_50\n",
      "108 conv2d_55\n",
      "109 batch_normalization_36\n",
      "110 batch_normalization_41\n",
      "111 activation_81\n",
      "112 activation_86\n",
      "113 conv2d_51\n",
      "114 conv2d_56\n",
      "115 batch_normalization_37\n",
      "116 batch_normalization_42\n",
      "117 activation_82\n",
      "118 activation_87\n",
      "119 average_pooling2d_4\n",
      "120 conv2d_49\n",
      "121 conv2d_52\n",
      "122 conv2d_57\n",
      "123 conv2d_58\n",
      "124 batch_normalization_35\n",
      "125 batch_normalization_38\n",
      "126 batch_normalization_43\n",
      "127 batch_normalization_44\n",
      "128 activation_80\n",
      "129 activation_83\n",
      "130 activation_88\n",
      "131 activation_89\n",
      "132 mixed4\n",
      "133 conv2d_63\n",
      "134 batch_normalization_49\n",
      "135 activation_94\n",
      "136 conv2d_64\n",
      "137 batch_normalization_50\n",
      "138 activation_95\n",
      "139 conv2d_60\n",
      "140 conv2d_65\n",
      "141 batch_normalization_46\n",
      "142 batch_normalization_51\n",
      "143 activation_91\n",
      "144 activation_96\n",
      "145 conv2d_61\n",
      "146 conv2d_66\n",
      "147 batch_normalization_47\n",
      "148 batch_normalization_52\n",
      "149 activation_92\n",
      "150 activation_97\n",
      "151 average_pooling2d_5\n",
      "152 conv2d_59\n",
      "153 conv2d_62\n",
      "154 conv2d_67\n",
      "155 conv2d_68\n",
      "156 batch_normalization_45\n",
      "157 batch_normalization_48\n",
      "158 batch_normalization_53\n",
      "159 batch_normalization_54\n",
      "160 activation_90\n",
      "161 activation_93\n",
      "162 activation_98\n",
      "163 activation_99\n",
      "164 mixed5\n",
      "165 conv2d_73\n",
      "166 batch_normalization_59\n",
      "167 activation_104\n",
      "168 conv2d_74\n",
      "169 batch_normalization_60\n",
      "170 activation_105\n",
      "171 conv2d_70\n",
      "172 conv2d_75\n",
      "173 batch_normalization_56\n",
      "174 batch_normalization_61\n",
      "175 activation_101\n",
      "176 activation_106\n",
      "177 conv2d_71\n",
      "178 conv2d_76\n",
      "179 batch_normalization_57\n",
      "180 batch_normalization_62\n",
      "181 activation_102\n",
      "182 activation_107\n",
      "183 average_pooling2d_6\n",
      "184 conv2d_69\n",
      "185 conv2d_72\n",
      "186 conv2d_77\n",
      "187 conv2d_78\n",
      "188 batch_normalization_55\n",
      "189 batch_normalization_58\n",
      "190 batch_normalization_63\n",
      "191 batch_normalization_64\n",
      "192 activation_100\n",
      "193 activation_103\n",
      "194 activation_108\n",
      "195 activation_109\n",
      "196 mixed6\n",
      "197 conv2d_83\n",
      "198 batch_normalization_69\n",
      "199 activation_114\n",
      "200 conv2d_84\n",
      "201 batch_normalization_70\n",
      "202 activation_115\n",
      "203 conv2d_80\n",
      "204 conv2d_85\n",
      "205 batch_normalization_66\n",
      "206 batch_normalization_71\n",
      "207 activation_111\n",
      "208 activation_116\n",
      "209 conv2d_81\n",
      "210 conv2d_86\n",
      "211 batch_normalization_67\n",
      "212 batch_normalization_72\n",
      "213 activation_112\n",
      "214 activation_117\n",
      "215 average_pooling2d_7\n",
      "216 conv2d_79\n",
      "217 conv2d_82\n",
      "218 conv2d_87\n",
      "219 conv2d_88\n",
      "220 batch_normalization_65\n",
      "221 batch_normalization_68\n",
      "222 batch_normalization_73\n",
      "223 batch_normalization_74\n",
      "224 activation_110\n",
      "225 activation_113\n",
      "226 activation_118\n",
      "227 activation_119\n",
      "228 mixed7\n",
      "229 conv2d_91\n",
      "230 batch_normalization_77\n",
      "231 activation_122\n",
      "232 conv2d_92\n",
      "233 batch_normalization_78\n",
      "234 activation_123\n",
      "235 conv2d_89\n",
      "236 conv2d_93\n",
      "237 batch_normalization_75\n",
      "238 batch_normalization_79\n",
      "239 activation_120\n",
      "240 activation_124\n",
      "241 conv2d_90\n",
      "242 conv2d_94\n",
      "243 batch_normalization_76\n",
      "244 batch_normalization_80\n",
      "245 activation_121\n",
      "246 activation_125\n",
      "247 max_pooling2d_9\n",
      "248 mixed8\n",
      "249 conv2d_99\n",
      "250 batch_normalization_85\n",
      "251 activation_130\n",
      "252 conv2d_96\n",
      "253 conv2d_100\n",
      "254 batch_normalization_82\n",
      "255 batch_normalization_86\n",
      "256 activation_127\n",
      "257 activation_131\n",
      "258 conv2d_97\n",
      "259 conv2d_98\n",
      "260 conv2d_101\n",
      "261 conv2d_102\n",
      "262 average_pooling2d_8\n",
      "263 conv2d_95\n",
      "264 batch_normalization_83\n",
      "265 batch_normalization_84\n",
      "266 batch_normalization_87\n",
      "267 batch_normalization_88\n",
      "268 conv2d_103\n",
      "269 batch_normalization_81\n",
      "270 activation_128\n",
      "271 activation_129\n",
      "272 activation_132\n",
      "273 activation_133\n",
      "274 batch_normalization_89\n",
      "275 activation_126\n",
      "276 mixed9_0\n",
      "277 concatenate_1\n",
      "278 activation_134\n",
      "279 mixed9\n",
      "280 conv2d_108\n",
      "281 batch_normalization_94\n",
      "282 activation_139\n",
      "283 conv2d_105\n",
      "284 conv2d_109\n",
      "285 batch_normalization_91\n",
      "286 batch_normalization_95\n",
      "287 activation_136\n",
      "288 activation_140\n",
      "289 conv2d_106\n",
      "290 conv2d_107\n",
      "291 conv2d_110\n",
      "292 conv2d_111\n",
      "293 average_pooling2d_9\n",
      "294 conv2d_104\n",
      "295 batch_normalization_92\n",
      "296 batch_normalization_93\n",
      "297 batch_normalization_96\n",
      "298 batch_normalization_97\n",
      "299 conv2d_112\n",
      "300 batch_normalization_90\n",
      "301 activation_137\n",
      "302 activation_138\n",
      "303 activation_141\n",
      "304 activation_142\n",
      "305 batch_normalization_98\n",
      "306 activation_135\n",
      "307 mixed9_1\n",
      "308 concatenate_2\n",
      "309 activation_143\n",
      "310 mixed10\n",
      "311 global_average_pooling2d_7\n",
      "312 dense_13\n",
      "313 dropout_7\n",
      "314 dense_14\n"
     ]
    }
   ],
   "source": [
    "for i,layer in enumerate(incep_model.layers):\n",
    "    print(i,layer.name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 input_6\n",
      "1 conv2d_113\n",
      "2 batch_normalization_99\n",
      "3 activation_144\n",
      "4 conv2d_114\n",
      "5 batch_normalization_100\n",
      "6 activation_145\n",
      "7 conv2d_115\n",
      "8 batch_normalization_101\n",
      "9 activation_146\n",
      "10 max_pooling2d_10\n",
      "11 conv2d_116\n",
      "12 batch_normalization_102\n",
      "13 activation_147\n",
      "14 conv2d_117\n",
      "15 batch_normalization_103\n",
      "16 activation_148\n",
      "17 max_pooling2d_11\n",
      "18 conv2d_121\n",
      "19 batch_normalization_107\n",
      "20 activation_152\n",
      "21 conv2d_119\n",
      "22 conv2d_122\n",
      "23 batch_normalization_105\n",
      "24 batch_normalization_108\n",
      "25 activation_150\n",
      "26 activation_153\n",
      "27 average_pooling2d_10\n",
      "28 conv2d_118\n",
      "29 conv2d_120\n",
      "30 conv2d_123\n",
      "31 conv2d_124\n",
      "32 batch_normalization_104\n",
      "33 batch_normalization_106\n",
      "34 batch_normalization_109\n",
      "35 batch_normalization_110\n",
      "36 activation_149\n",
      "37 activation_151\n",
      "38 activation_154\n",
      "39 activation_155\n",
      "40 mixed_5b\n",
      "41 conv2d_128\n",
      "42 batch_normalization_114\n",
      "43 activation_159\n",
      "44 conv2d_126\n",
      "45 conv2d_129\n",
      "46 batch_normalization_112\n",
      "47 batch_normalization_115\n",
      "48 activation_157\n",
      "49 activation_160\n",
      "50 conv2d_125\n",
      "51 conv2d_127\n",
      "52 conv2d_130\n",
      "53 batch_normalization_111\n",
      "54 batch_normalization_113\n",
      "55 batch_normalization_116\n",
      "56 activation_156\n",
      "57 activation_158\n",
      "58 activation_161\n",
      "59 block35_1_mixed\n",
      "60 block35_1_conv\n",
      "61 block35_1\n",
      "62 block35_1_ac\n",
      "63 conv2d_134\n",
      "64 batch_normalization_120\n",
      "65 activation_165\n",
      "66 conv2d_132\n",
      "67 conv2d_135\n",
      "68 batch_normalization_118\n",
      "69 batch_normalization_121\n",
      "70 activation_163\n",
      "71 activation_166\n",
      "72 conv2d_131\n",
      "73 conv2d_133\n",
      "74 conv2d_136\n",
      "75 batch_normalization_117\n",
      "76 batch_normalization_119\n",
      "77 batch_normalization_122\n",
      "78 activation_162\n",
      "79 activation_164\n",
      "80 activation_167\n",
      "81 block35_2_mixed\n",
      "82 block35_2_conv\n",
      "83 block35_2\n",
      "84 block35_2_ac\n",
      "85 conv2d_140\n",
      "86 batch_normalization_126\n",
      "87 activation_171\n",
      "88 conv2d_138\n",
      "89 conv2d_141\n",
      "90 batch_normalization_124\n",
      "91 batch_normalization_127\n",
      "92 activation_169\n",
      "93 activation_172\n",
      "94 conv2d_137\n",
      "95 conv2d_139\n",
      "96 conv2d_142\n",
      "97 batch_normalization_123\n",
      "98 batch_normalization_125\n",
      "99 batch_normalization_128\n",
      "100 activation_168\n",
      "101 activation_170\n",
      "102 activation_173\n",
      "103 block35_3_mixed\n",
      "104 block35_3_conv\n",
      "105 block35_3\n",
      "106 block35_3_ac\n",
      "107 conv2d_146\n",
      "108 batch_normalization_132\n",
      "109 activation_177\n",
      "110 conv2d_144\n",
      "111 conv2d_147\n",
      "112 batch_normalization_130\n",
      "113 batch_normalization_133\n",
      "114 activation_175\n",
      "115 activation_178\n",
      "116 conv2d_143\n",
      "117 conv2d_145\n",
      "118 conv2d_148\n",
      "119 batch_normalization_129\n",
      "120 batch_normalization_131\n",
      "121 batch_normalization_134\n",
      "122 activation_174\n",
      "123 activation_176\n",
      "124 activation_179\n",
      "125 block35_4_mixed\n",
      "126 block35_4_conv\n",
      "127 block35_4\n",
      "128 block35_4_ac\n",
      "129 conv2d_152\n",
      "130 batch_normalization_138\n",
      "131 activation_183\n",
      "132 conv2d_150\n",
      "133 conv2d_153\n",
      "134 batch_normalization_136\n",
      "135 batch_normalization_139\n",
      "136 activation_181\n",
      "137 activation_184\n",
      "138 conv2d_149\n",
      "139 conv2d_151\n",
      "140 conv2d_154\n",
      "141 batch_normalization_135\n",
      "142 batch_normalization_137\n",
      "143 batch_normalization_140\n",
      "144 activation_180\n",
      "145 activation_182\n",
      "146 activation_185\n",
      "147 block35_5_mixed\n",
      "148 block35_5_conv\n",
      "149 block35_5\n",
      "150 block35_5_ac\n",
      "151 conv2d_158\n",
      "152 batch_normalization_144\n",
      "153 activation_189\n",
      "154 conv2d_156\n",
      "155 conv2d_159\n",
      "156 batch_normalization_142\n",
      "157 batch_normalization_145\n",
      "158 activation_187\n",
      "159 activation_190\n",
      "160 conv2d_155\n",
      "161 conv2d_157\n",
      "162 conv2d_160\n",
      "163 batch_normalization_141\n",
      "164 batch_normalization_143\n",
      "165 batch_normalization_146\n",
      "166 activation_186\n",
      "167 activation_188\n",
      "168 activation_191\n",
      "169 block35_6_mixed\n",
      "170 block35_6_conv\n",
      "171 block35_6\n",
      "172 block35_6_ac\n",
      "173 conv2d_164\n",
      "174 batch_normalization_150\n",
      "175 activation_195\n",
      "176 conv2d_162\n",
      "177 conv2d_165\n",
      "178 batch_normalization_148\n",
      "179 batch_normalization_151\n",
      "180 activation_193\n",
      "181 activation_196\n",
      "182 conv2d_161\n",
      "183 conv2d_163\n",
      "184 conv2d_166\n",
      "185 batch_normalization_147\n",
      "186 batch_normalization_149\n",
      "187 batch_normalization_152\n",
      "188 activation_192\n",
      "189 activation_194\n",
      "190 activation_197\n",
      "191 block35_7_mixed\n",
      "192 block35_7_conv\n",
      "193 block35_7\n",
      "194 block35_7_ac\n",
      "195 conv2d_170\n",
      "196 batch_normalization_156\n",
      "197 activation_201\n",
      "198 conv2d_168\n",
      "199 conv2d_171\n",
      "200 batch_normalization_154\n",
      "201 batch_normalization_157\n",
      "202 activation_199\n",
      "203 activation_202\n",
      "204 conv2d_167\n",
      "205 conv2d_169\n",
      "206 conv2d_172\n",
      "207 batch_normalization_153\n",
      "208 batch_normalization_155\n",
      "209 batch_normalization_158\n",
      "210 activation_198\n",
      "211 activation_200\n",
      "212 activation_203\n",
      "213 block35_8_mixed\n",
      "214 block35_8_conv\n",
      "215 block35_8\n",
      "216 block35_8_ac\n",
      "217 conv2d_176\n",
      "218 batch_normalization_162\n",
      "219 activation_207\n",
      "220 conv2d_174\n",
      "221 conv2d_177\n",
      "222 batch_normalization_160\n",
      "223 batch_normalization_163\n",
      "224 activation_205\n",
      "225 activation_208\n",
      "226 conv2d_173\n",
      "227 conv2d_175\n",
      "228 conv2d_178\n",
      "229 batch_normalization_159\n",
      "230 batch_normalization_161\n",
      "231 batch_normalization_164\n",
      "232 activation_204\n",
      "233 activation_206\n",
      "234 activation_209\n",
      "235 block35_9_mixed\n",
      "236 block35_9_conv\n",
      "237 block35_9\n",
      "238 block35_9_ac\n",
      "239 conv2d_182\n",
      "240 batch_normalization_168\n",
      "241 activation_213\n",
      "242 conv2d_180\n",
      "243 conv2d_183\n",
      "244 batch_normalization_166\n",
      "245 batch_normalization_169\n",
      "246 activation_211\n",
      "247 activation_214\n",
      "248 conv2d_179\n",
      "249 conv2d_181\n",
      "250 conv2d_184\n",
      "251 batch_normalization_165\n",
      "252 batch_normalization_167\n",
      "253 batch_normalization_170\n",
      "254 activation_210\n",
      "255 activation_212\n",
      "256 activation_215\n",
      "257 block35_10_mixed\n",
      "258 block35_10_conv\n",
      "259 block35_10\n",
      "260 block35_10_ac\n",
      "261 conv2d_186\n",
      "262 batch_normalization_172\n",
      "263 activation_217\n",
      "264 conv2d_187\n",
      "265 batch_normalization_173\n",
      "266 activation_218\n",
      "267 conv2d_185\n",
      "268 conv2d_188\n",
      "269 batch_normalization_171\n",
      "270 batch_normalization_174\n",
      "271 activation_216\n",
      "272 activation_219\n",
      "273 max_pooling2d_12\n",
      "274 mixed_6a\n",
      "275 conv2d_190\n",
      "276 batch_normalization_176\n",
      "277 activation_221\n",
      "278 conv2d_191\n",
      "279 batch_normalization_177\n",
      "280 activation_222\n",
      "281 conv2d_189\n",
      "282 conv2d_192\n",
      "283 batch_normalization_175\n",
      "284 batch_normalization_178\n",
      "285 activation_220\n",
      "286 activation_223\n",
      "287 block17_1_mixed\n",
      "288 block17_1_conv\n",
      "289 block17_1\n",
      "290 block17_1_ac\n",
      "291 conv2d_194\n",
      "292 batch_normalization_180\n",
      "293 activation_225\n",
      "294 conv2d_195\n",
      "295 batch_normalization_181\n",
      "296 activation_226\n",
      "297 conv2d_193\n",
      "298 conv2d_196\n",
      "299 batch_normalization_179\n",
      "300 batch_normalization_182\n",
      "301 activation_224\n",
      "302 activation_227\n",
      "303 block17_2_mixed\n",
      "304 block17_2_conv\n",
      "305 block17_2\n",
      "306 block17_2_ac\n",
      "307 conv2d_198\n",
      "308 batch_normalization_184\n",
      "309 activation_229\n",
      "310 conv2d_199\n",
      "311 batch_normalization_185\n",
      "312 activation_230\n",
      "313 conv2d_197\n",
      "314 conv2d_200\n",
      "315 batch_normalization_183\n",
      "316 batch_normalization_186\n",
      "317 activation_228\n",
      "318 activation_231\n",
      "319 block17_3_mixed\n",
      "320 block17_3_conv\n",
      "321 block17_3\n",
      "322 block17_3_ac\n",
      "323 conv2d_202\n",
      "324 batch_normalization_188\n",
      "325 activation_233\n",
      "326 conv2d_203\n",
      "327 batch_normalization_189\n",
      "328 activation_234\n",
      "329 conv2d_201\n",
      "330 conv2d_204\n",
      "331 batch_normalization_187\n",
      "332 batch_normalization_190\n",
      "333 activation_232\n",
      "334 activation_235\n",
      "335 block17_4_mixed\n",
      "336 block17_4_conv\n",
      "337 block17_4\n",
      "338 block17_4_ac\n",
      "339 conv2d_206\n",
      "340 batch_normalization_192\n",
      "341 activation_237\n",
      "342 conv2d_207\n",
      "343 batch_normalization_193\n",
      "344 activation_238\n",
      "345 conv2d_205\n",
      "346 conv2d_208\n",
      "347 batch_normalization_191\n",
      "348 batch_normalization_194\n",
      "349 activation_236\n",
      "350 activation_239\n",
      "351 block17_5_mixed\n",
      "352 block17_5_conv\n",
      "353 block17_5\n",
      "354 block17_5_ac\n",
      "355 conv2d_210\n",
      "356 batch_normalization_196\n",
      "357 activation_241\n",
      "358 conv2d_211\n",
      "359 batch_normalization_197\n",
      "360 activation_242\n",
      "361 conv2d_209\n",
      "362 conv2d_212\n",
      "363 batch_normalization_195\n",
      "364 batch_normalization_198\n",
      "365 activation_240\n",
      "366 activation_243\n",
      "367 block17_6_mixed\n",
      "368 block17_6_conv\n",
      "369 block17_6\n",
      "370 block17_6_ac\n",
      "371 conv2d_214\n",
      "372 batch_normalization_200\n",
      "373 activation_245\n",
      "374 conv2d_215\n",
      "375 batch_normalization_201\n",
      "376 activation_246\n",
      "377 conv2d_213\n",
      "378 conv2d_216\n",
      "379 batch_normalization_199\n",
      "380 batch_normalization_202\n",
      "381 activation_244\n",
      "382 activation_247\n",
      "383 block17_7_mixed\n",
      "384 block17_7_conv\n",
      "385 block17_7\n",
      "386 block17_7_ac\n",
      "387 conv2d_218\n",
      "388 batch_normalization_204\n",
      "389 activation_249\n",
      "390 conv2d_219\n",
      "391 batch_normalization_205\n",
      "392 activation_250\n",
      "393 conv2d_217\n",
      "394 conv2d_220\n",
      "395 batch_normalization_203\n",
      "396 batch_normalization_206\n",
      "397 activation_248\n",
      "398 activation_251\n",
      "399 block17_8_mixed\n",
      "400 block17_8_conv\n",
      "401 block17_8\n",
      "402 block17_8_ac\n",
      "403 conv2d_222\n",
      "404 batch_normalization_208\n",
      "405 activation_253\n",
      "406 conv2d_223\n",
      "407 batch_normalization_209\n",
      "408 activation_254\n",
      "409 conv2d_221\n",
      "410 conv2d_224\n",
      "411 batch_normalization_207\n",
      "412 batch_normalization_210\n",
      "413 activation_252\n",
      "414 activation_255\n",
      "415 block17_9_mixed\n",
      "416 block17_9_conv\n",
      "417 block17_9\n",
      "418 block17_9_ac\n",
      "419 conv2d_226\n",
      "420 batch_normalization_212\n",
      "421 activation_257\n",
      "422 conv2d_227\n",
      "423 batch_normalization_213\n",
      "424 activation_258\n",
      "425 conv2d_225\n",
      "426 conv2d_228\n",
      "427 batch_normalization_211\n",
      "428 batch_normalization_214\n",
      "429 activation_256\n",
      "430 activation_259\n",
      "431 block17_10_mixed\n",
      "432 block17_10_conv\n",
      "433 block17_10\n",
      "434 block17_10_ac\n",
      "435 conv2d_230\n",
      "436 batch_normalization_216\n",
      "437 activation_261\n",
      "438 conv2d_231\n",
      "439 batch_normalization_217\n",
      "440 activation_262\n",
      "441 conv2d_229\n",
      "442 conv2d_232\n",
      "443 batch_normalization_215\n",
      "444 batch_normalization_218\n",
      "445 activation_260\n",
      "446 activation_263\n",
      "447 block17_11_mixed\n",
      "448 block17_11_conv\n",
      "449 block17_11\n",
      "450 block17_11_ac\n",
      "451 conv2d_234\n",
      "452 batch_normalization_220\n",
      "453 activation_265\n",
      "454 conv2d_235\n",
      "455 batch_normalization_221\n",
      "456 activation_266\n",
      "457 conv2d_233\n",
      "458 conv2d_236\n",
      "459 batch_normalization_219\n",
      "460 batch_normalization_222\n",
      "461 activation_264\n",
      "462 activation_267\n",
      "463 block17_12_mixed\n",
      "464 block17_12_conv\n",
      "465 block17_12\n",
      "466 block17_12_ac\n",
      "467 conv2d_238\n",
      "468 batch_normalization_224\n",
      "469 activation_269\n",
      "470 conv2d_239\n",
      "471 batch_normalization_225\n",
      "472 activation_270\n",
      "473 conv2d_237\n",
      "474 conv2d_240\n",
      "475 batch_normalization_223\n",
      "476 batch_normalization_226\n",
      "477 activation_268\n",
      "478 activation_271\n",
      "479 block17_13_mixed\n",
      "480 block17_13_conv\n",
      "481 block17_13\n",
      "482 block17_13_ac\n",
      "483 conv2d_242\n",
      "484 batch_normalization_228\n",
      "485 activation_273\n",
      "486 conv2d_243\n",
      "487 batch_normalization_229\n",
      "488 activation_274\n",
      "489 conv2d_241\n",
      "490 conv2d_244\n",
      "491 batch_normalization_227\n",
      "492 batch_normalization_230\n",
      "493 activation_272\n",
      "494 activation_275\n",
      "495 block17_14_mixed\n",
      "496 block17_14_conv\n",
      "497 block17_14\n",
      "498 block17_14_ac\n",
      "499 conv2d_246\n",
      "500 batch_normalization_232\n",
      "501 activation_277\n",
      "502 conv2d_247\n",
      "503 batch_normalization_233\n",
      "504 activation_278\n",
      "505 conv2d_245\n",
      "506 conv2d_248\n",
      "507 batch_normalization_231\n",
      "508 batch_normalization_234\n",
      "509 activation_276\n",
      "510 activation_279\n",
      "511 block17_15_mixed\n",
      "512 block17_15_conv\n",
      "513 block17_15\n",
      "514 block17_15_ac\n",
      "515 conv2d_250\n",
      "516 batch_normalization_236\n",
      "517 activation_281\n",
      "518 conv2d_251\n",
      "519 batch_normalization_237\n",
      "520 activation_282\n",
      "521 conv2d_249\n",
      "522 conv2d_252\n",
      "523 batch_normalization_235\n",
      "524 batch_normalization_238\n",
      "525 activation_280\n",
      "526 activation_283\n",
      "527 block17_16_mixed\n",
      "528 block17_16_conv\n",
      "529 block17_16\n",
      "530 block17_16_ac\n",
      "531 conv2d_254\n",
      "532 batch_normalization_240\n",
      "533 activation_285\n",
      "534 conv2d_255\n",
      "535 batch_normalization_241\n",
      "536 activation_286\n",
      "537 conv2d_253\n",
      "538 conv2d_256\n",
      "539 batch_normalization_239\n",
      "540 batch_normalization_242\n",
      "541 activation_284\n",
      "542 activation_287\n",
      "543 block17_17_mixed\n",
      "544 block17_17_conv\n",
      "545 block17_17\n",
      "546 block17_17_ac\n",
      "547 conv2d_258\n",
      "548 batch_normalization_244\n",
      "549 activation_289\n",
      "550 conv2d_259\n",
      "551 batch_normalization_245\n",
      "552 activation_290\n",
      "553 conv2d_257\n",
      "554 conv2d_260\n",
      "555 batch_normalization_243\n",
      "556 batch_normalization_246\n",
      "557 activation_288\n",
      "558 activation_291\n",
      "559 block17_18_mixed\n",
      "560 block17_18_conv\n",
      "561 block17_18\n",
      "562 block17_18_ac\n",
      "563 conv2d_262\n",
      "564 batch_normalization_248\n",
      "565 activation_293\n",
      "566 conv2d_263\n",
      "567 batch_normalization_249\n",
      "568 activation_294\n",
      "569 conv2d_261\n",
      "570 conv2d_264\n",
      "571 batch_normalization_247\n",
      "572 batch_normalization_250\n",
      "573 activation_292\n",
      "574 activation_295\n",
      "575 block17_19_mixed\n",
      "576 block17_19_conv\n",
      "577 block17_19\n",
      "578 block17_19_ac\n",
      "579 conv2d_266\n",
      "580 batch_normalization_252\n",
      "581 activation_297\n",
      "582 conv2d_267\n",
      "583 batch_normalization_253\n",
      "584 activation_298\n",
      "585 conv2d_265\n",
      "586 conv2d_268\n",
      "587 batch_normalization_251\n",
      "588 batch_normalization_254\n",
      "589 activation_296\n",
      "590 activation_299\n",
      "591 block17_20_mixed\n",
      "592 block17_20_conv\n",
      "593 block17_20\n",
      "594 block17_20_ac\n",
      "595 conv2d_273\n",
      "596 batch_normalization_259\n",
      "597 activation_304\n",
      "598 conv2d_269\n",
      "599 conv2d_271\n",
      "600 conv2d_274\n",
      "601 batch_normalization_255\n",
      "602 batch_normalization_257\n",
      "603 batch_normalization_260\n",
      "604 activation_300\n",
      "605 activation_302\n",
      "606 activation_305\n",
      "607 conv2d_270\n",
      "608 conv2d_272\n",
      "609 conv2d_275\n",
      "610 batch_normalization_256\n",
      "611 batch_normalization_258\n",
      "612 batch_normalization_261\n",
      "613 activation_301\n",
      "614 activation_303\n",
      "615 activation_306\n",
      "616 max_pooling2d_13\n",
      "617 mixed_7a\n",
      "618 conv2d_277\n",
      "619 batch_normalization_263\n",
      "620 activation_308\n",
      "621 conv2d_278\n",
      "622 batch_normalization_264\n",
      "623 activation_309\n",
      "624 conv2d_276\n",
      "625 conv2d_279\n",
      "626 batch_normalization_262\n",
      "627 batch_normalization_265\n",
      "628 activation_307\n",
      "629 activation_310\n",
      "630 block8_1_mixed\n",
      "631 block8_1_conv\n",
      "632 block8_1\n",
      "633 block8_1_ac\n",
      "634 conv2d_281\n",
      "635 batch_normalization_267\n",
      "636 activation_312\n",
      "637 conv2d_282\n",
      "638 batch_normalization_268\n",
      "639 activation_313\n",
      "640 conv2d_280\n",
      "641 conv2d_283\n",
      "642 batch_normalization_266\n",
      "643 batch_normalization_269\n",
      "644 activation_311\n",
      "645 activation_314\n",
      "646 block8_2_mixed\n",
      "647 block8_2_conv\n",
      "648 block8_2\n",
      "649 block8_2_ac\n",
      "650 conv2d_285\n",
      "651 batch_normalization_271\n",
      "652 activation_316\n",
      "653 conv2d_286\n",
      "654 batch_normalization_272\n",
      "655 activation_317\n",
      "656 conv2d_284\n",
      "657 conv2d_287\n",
      "658 batch_normalization_270\n",
      "659 batch_normalization_273\n",
      "660 activation_315\n",
      "661 activation_318\n",
      "662 block8_3_mixed\n",
      "663 block8_3_conv\n",
      "664 block8_3\n",
      "665 block8_3_ac\n",
      "666 conv2d_289\n",
      "667 batch_normalization_275\n",
      "668 activation_320\n",
      "669 conv2d_290\n",
      "670 batch_normalization_276\n",
      "671 activation_321\n",
      "672 conv2d_288\n",
      "673 conv2d_291\n",
      "674 batch_normalization_274\n",
      "675 batch_normalization_277\n",
      "676 activation_319\n",
      "677 activation_322\n",
      "678 block8_4_mixed\n",
      "679 block8_4_conv\n",
      "680 block8_4\n",
      "681 block8_4_ac\n",
      "682 conv2d_293\n",
      "683 batch_normalization_279\n",
      "684 activation_324\n",
      "685 conv2d_294\n",
      "686 batch_normalization_280\n",
      "687 activation_325\n",
      "688 conv2d_292\n",
      "689 conv2d_295\n",
      "690 batch_normalization_278\n",
      "691 batch_normalization_281\n",
      "692 activation_323\n",
      "693 activation_326\n",
      "694 block8_5_mixed\n",
      "695 block8_5_conv\n",
      "696 block8_5\n",
      "697 block8_5_ac\n",
      "698 conv2d_297\n",
      "699 batch_normalization_283\n",
      "700 activation_328\n",
      "701 conv2d_298\n",
      "702 batch_normalization_284\n",
      "703 activation_329\n",
      "704 conv2d_296\n",
      "705 conv2d_299\n",
      "706 batch_normalization_282\n",
      "707 batch_normalization_285\n",
      "708 activation_327\n",
      "709 activation_330\n",
      "710 block8_6_mixed\n",
      "711 block8_6_conv\n",
      "712 block8_6\n",
      "713 block8_6_ac\n",
      "714 conv2d_301\n",
      "715 batch_normalization_287\n",
      "716 activation_332\n",
      "717 conv2d_302\n",
      "718 batch_normalization_288\n",
      "719 activation_333\n",
      "720 conv2d_300\n",
      "721 conv2d_303\n",
      "722 batch_normalization_286\n",
      "723 batch_normalization_289\n",
      "724 activation_331\n",
      "725 activation_334\n",
      "726 block8_7_mixed\n",
      "727 block8_7_conv\n",
      "728 block8_7\n",
      "729 block8_7_ac\n",
      "730 conv2d_305\n",
      "731 batch_normalization_291\n",
      "732 activation_336\n",
      "733 conv2d_306\n",
      "734 batch_normalization_292\n",
      "735 activation_337\n",
      "736 conv2d_304\n",
      "737 conv2d_307\n",
      "738 batch_normalization_290\n",
      "739 batch_normalization_293\n",
      "740 activation_335\n",
      "741 activation_338\n",
      "742 block8_8_mixed\n",
      "743 block8_8_conv\n",
      "744 block8_8\n",
      "745 block8_8_ac\n",
      "746 conv2d_309\n",
      "747 batch_normalization_295\n",
      "748 activation_340\n",
      "749 conv2d_310\n",
      "750 batch_normalization_296\n",
      "751 activation_341\n",
      "752 conv2d_308\n",
      "753 conv2d_311\n",
      "754 batch_normalization_294\n",
      "755 batch_normalization_297\n",
      "756 activation_339\n",
      "757 activation_342\n",
      "758 block8_9_mixed\n",
      "759 block8_9_conv\n",
      "760 block8_9\n",
      "761 block8_9_ac\n",
      "762 conv2d_313\n",
      "763 batch_normalization_299\n",
      "764 activation_344\n",
      "765 conv2d_314\n",
      "766 batch_normalization_300\n",
      "767 activation_345\n",
      "768 conv2d_312\n",
      "769 conv2d_315\n",
      "770 batch_normalization_298\n",
      "771 batch_normalization_301\n",
      "772 activation_343\n",
      "773 activation_346\n",
      "774 block8_10_mixed\n",
      "775 block8_10_conv\n",
      "776 block8_10\n",
      "777 conv_7b\n",
      "778 conv_7b_bn\n",
      "779 conv_7b_ac\n",
      "780 global_average_pooling2d_8\n",
      "781 dense_15\n",
      "782 dropout_8\n",
      "783 dense_16\n"
     ]
    }
   ],
   "source": [
    "for i,layer in enumerate(inres_model.layers):\n",
    "    print(i,layer.name)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Construct the ensemble model using the last \"dense layer\" of each base CNN model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "\n",
    "model1=Model(inputs=[xception_model.layers[0].get_input_at(0)],outputs=xception_model.get_layer('dense_6').output,name='xception')\n",
    "model2=Model(inputs=[vgg_model.layers[0].get_input_at(0)],outputs=vgg_model.get_layer('dense_8').output,name='vgg')\n",
    "model3=Model(inputs=[vgg19_model.layers[0].get_input_at(0)],outputs=vgg19_model.get_layer('dense_10').output,name='vgg19')\n",
    "model4=Model(inputs=[incep_model.layers[0].get_input_at(0)],outputs=incep_model.get_layer('dense_14').output,name='incep')\n",
    "model5=Model(inputs=[inres_model.layers[0].get_input_at(0)],outputs=inres_model.get_layer('dense_16').output,name='inres')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#plot the figures\n",
    "class LossHistory(keras.callbacks.Callback):\n",
    "    def on_train_begin(self, logs={}):\n",
    "        self.losses = {'batch':[], 'epoch':[]}\n",
    "        self.accuracy = {'batch':[], 'epoch':[]}\n",
    "        self.val_loss = {'batch':[], 'epoch':[]}\n",
    "        self.val_acc = {'batch':[], 'epoch':[]}\n",
    "    def on_batch_end(self, batch, logs={}):\n",
    "        self.losses['batch'].append(logs.get('loss'))\n",
    "        self.accuracy['batch'].append(logs.get('acc'))\n",
    "        self.val_loss['batch'].append(logs.get('val_loss'))\n",
    "        self.val_acc['batch'].append(logs.get('val_acc'))\n",
    "    def on_epoch_end(self, batch, logs={}):\n",
    "        self.losses['epoch'].append(logs.get('loss'))\n",
    "        self.accuracy['epoch'].append(logs.get('acc'))\n",
    "        self.val_loss['epoch'].append(logs.get('val_loss'))\n",
    "        self.val_acc['epoch'].append(logs.get('val_acc'))\n",
    "    def loss_plot(self, loss_type):\n",
    "        iters = range(len(self.losses[loss_type]))\n",
    "        plt.figure()\n",
    "        plt.plot(iters, self.losses[loss_type], 'g', label='train loss')\n",
    "        if loss_type == 'epoch':\n",
    "            # acc\n",
    "            plt.plot(iters, self.accuracy[loss_type], 'r', label='train acc')\n",
    "            # loss\n",
    "            plt.plot(iters, self.losses[loss_type], 'g', label='train loss')\n",
    "            # val_acc\n",
    "            plt.plot(iters, self.val_acc[loss_type], 'b', label='val acc')\n",
    "            # val_loss\n",
    "            plt.plot(iters, self.val_loss[loss_type], 'k', label='val loss')\n",
    "        plt.grid(True)\n",
    "        plt.xlabel(loss_type)\n",
    "        plt.ylabel('acc-loss')\n",
    "        plt.legend(loc=\"upper right\")\n",
    "        plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "ensemble_history= LossHistory()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Found 23382 images belonging to 5 classes.\n",
      "Found 5845 images belonging to 5 classes.\n"
     ]
    }
   ],
   "source": [
    "#generate training and test images\n",
    "TARGET_SIZE=(224,224)\n",
    "INPUT_SIZE=(224,224,3)\n",
    "BATCHSIZE=128\t#could try 128 or 32\n",
    "\n",
    "#Normalization\n",
    "train_datagen = ImageDataGenerator(rescale=1./255)\n",
    "\n",
    "test_datagen = ImageDataGenerator(rescale=1./255)\n",
    "\n",
    "train_generator = train_datagen.flow_from_directory(\n",
    "        './train_224/',\n",
    "        target_size=TARGET_SIZE,\n",
    "        batch_size=BATCHSIZE,\n",
    "        class_mode='categorical')\n",
    "validation_generator = test_datagen.flow_from_directory(\n",
    "        './test_224/',\n",
    "        target_size=TARGET_SIZE,\n",
    "        batch_size=BATCHSIZE,\n",
    "        class_mode='categorical')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def lr_decay(epoch):\n",
    "    lrs = [0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.0001,0.00001,0.000001,\n",
    "           0.000001,0.000001,0.000001,0.000001,0.0000001,0.0000001,0.0000001,0.0000001,0.0000001,0.0000001\n",
    "          ]\n",
    "    return lrs[epoch]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "auto_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=1, verbose=0, mode='auto', epsilon=0.0001, cooldown=0, min_lr=0)\n",
    "my_lr = LearningRateScheduler(lr_decay)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def ensemble(num_class,epochs,savepath='./ensemble.h5'):\n",
    "    img=Input(shape=(224,224,3),name='img')\n",
    "    feature1=model1(img)\n",
    "    feature2=model2(img)\n",
    "    feature3=model3(img)\n",
    "    x=concatenate([feature1,feature2,feature3])\n",
    "    x=Dropout(0.5)(x)\n",
    "    x=Dense(64,activation='relu')(x)\n",
    "    x=Dropout(0.25)(x)\n",
    "    output=Dense(num_class,activation='softmax',name='output')(x)\n",
    "    model=Model(inputs=img,outputs=output)\n",
    "    opt = keras.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-08)\n",
    "    model.compile(loss='categorical_crossentropy',\n",
    "                  optimizer=opt,\n",
    "                  metrics=['accuracy'])\n",
    "    #train model\n",
    "    earlyStopping=kcallbacks.EarlyStopping(monitor='val_acc',patience=2, verbose=1, mode='auto')\n",
    "    saveBestModel = kcallbacks.ModelCheckpoint(filepath=savepath, monitor='val_acc', verbose=1, save_best_only=True, mode='auto')\n",
    "    hist=model.fit_generator(\n",
    "        train_generator,\n",
    "        steps_per_epoch=len(train_generator),\n",
    "        epochs=epochs,\n",
    "        validation_data=validation_generator,\n",
    "        validation_steps=len(validation_generator),\n",
    "        callbacks=[earlyStopping,saveBestModel,ensemble_history,auto_lr],\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/20\n",
      "182/183 [============================>.] - ETA: 0s - loss: 0.5297 - acc: 0.8809Epoch 00001: val_acc improved from -inf to 1.00000, saving model to ./ensemble.h5\n",
      "183/183 [==============================] - 204s 1s/step - loss: 0.5278 - acc: 0.8814 - val_loss: 0.0590 - val_acc: 1.0000\n",
      "Epoch 2/20\n",
      "182/183 [============================>.] - ETA: 0s - loss: 0.1287 - acc: 0.9770Epoch 00002: val_acc did not improve\n",
      "183/183 [==============================] - 184s 1s/step - loss: 0.1286 - acc: 0.9770 - val_loss: 0.0103 - val_acc: 1.0000\n",
      "Epoch 3/20\n",
      "182/183 [============================>.] - ETA: 0s - loss: 0.0975 - acc: 0.9778Epoch 00003: val_acc did not improve\n",
      "183/183 [==============================] - 184s 1s/step - loss: 0.0973 - acc: 0.9778 - val_loss: 0.0033 - val_acc: 1.0000\n",
      "Epoch 00003: early stopping\n"
     ]
    }
   ],
   "source": [
    "ensemble_model=ensemble(num_class=5,epochs=20)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "ensemble_model=load_model('./ensemble.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 ./test_224/0\\100015.png\n"
     ]
    }
   ],
   "source": [
    "#read images from validation folder\n",
    "rootdir = './test_224/'\n",
    "test_laels = []\n",
    "test_images=[]\n",
    "for subdir, dirs, files in os.walk(rootdir):\n",
    "    for file in files:\n",
    "        if not (file.endswith(\".jpeg\"))|(file.endswith(\".jpg\"))|(file.endswith(\".png\")):\n",
    "            continue\n",
    "        test_laels.append(subdir.split('/')[-1])\n",
    "        test_images.append(os.path.join(subdir, file))\n",
    "        \n",
    "print(test_laels[0],test_images[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The testing time is :54.200418 seconds\n"
     ]
    }
   ],
   "source": [
    "#test the averaging model on the validation set\n",
    "import time\n",
    "predict=[]\n",
    "length=len(test_images)\n",
    "t1 = time.time()\n",
    "for i in range((length//127)+1):\n",
    "    inputimg=test_images[127*i:127*(i+1)]\n",
    "    test_batch=[]\n",
    "    for path in inputimg:\n",
    "        thisimg=np.array(Image.open(path))/255\n",
    "        test_batch.append(thisimg)\n",
    "    #print(i, np.array(test_batch).shape)\n",
    "    ensemble_model_batch=ensemble_model.predict(np.array(test_batch))\n",
    "    predict_batch=list(np.argmax(ensemble_model_batch,axis=1))\n",
    "    predict_batch=[label[con] for con in predict_batch]\n",
    "    predict.append(predict_batch)\n",
    "\n",
    "predict=sum(predict,[])\n",
    "\n",
    "t2 = time.time()\n",
    "print('The testing time is :%f seconds' % (t2-t1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Concatenation accuracy:1.0\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score\n",
    "acc=accuracy_score(test_laels,predict)\n",
    "print('Concatenation accuracy:%s'%acc)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[5052    0    0    0    0]\n",
      " [   0  225    0    0    0]\n",
      " [   0    0  200    0    0]\n",
      " [   0    0    0  197    0]\n",
      " [   0    0    0    0  171]]\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       1.00      1.00      1.00      5052\n",
      "           1       1.00      1.00      1.00       225\n",
      "           2       1.00      1.00      1.00       200\n",
      "           3       1.00      1.00      1.00       197\n",
      "           4       1.00      1.00      1.00       171\n",
      "\n",
      "    accuracy                           1.00      5845\n",
      "   macro avg       1.00      1.00      1.00      5845\n",
      "weighted avg       1.00      1.00      1.00      5845\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import classification_report, confusion_matrix\n",
    "print(confusion_matrix(test_laels, predict))\n",
    "target_names = ['0', '1','2','3','4']\n",
    "print(classification_report(test_laels, predict, target_names=target_names))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "tf36cnn",
   "language": "python",
   "name": "tf36cnn"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
